<template>
  <canvas :id="canvasId" :width="props.width" :height="props.height">
    Your browser does not support the canvas element.
  </canvas>
</template>
<script>
  export default {
    name: 'Dashboard',
  };
</script>
<script setup>
  import { defineProps, onMounted } from 'vue';
  import { isArray } from '@/utils/config.js';
  import { setIndex, getIndex } from './setIndex.js';
  const props = defineProps({
    // id
    id: {
      type: String,
      default: '',
    },
    // 画布宽
    width: {
      type: Number,
      default: 300,
    },
    // 画布高
    height: {
      type: Number,
      default: 150,
    },
    // 主标题
    title: {
      type: String,
      default: '',
    },
    // 副标题
    subTitle: {
      type: String,
      default: '',
    },
    // 主标题样式
    titleStyle: {
      type: Object,
      default: () => {},
    },
    // 副标题样式
    subTitleStyle: {
      type: Object,
      default: () => {},
    },
    // 进度线颜色
    color: {
      type: [String, Array],
      default: '#0dd7de',
    },
    // 进度条颜色
    backgroundColor: {
      type: [String, Array],
      default: 'rgba(75, 160, 174, 0.5)',
    },
    // 数值
    value: {
      type: Number,
      default: 0,
    },
    // 开始角度--不支持修改
    startAngle: {
      type: Number,
      default: 0,
    },
    // 结束角度--不支持修改
    endAngle: {
      type: Number,
      default: 180,
    },
    // 最大值
    maxValue: {
      type: Number,
      default: 100,
    },
    // 刻度份数
    splitNumber: {
      type: Number,
      default: 5,
    },
    // 刻度线宽度
    splitWidth: {
      type: Number,
      default: 2,
    },
    // 刻度值样式
    splitStyle: {
      type: Object,
      default: () => {},
    },
    // 刻度线颜色
    splitLineColor: {
      type: String,
      default: 'rgba(0, 0, 0, 0.5)',
    },
    // 内圆半径
    innerRadius: {
      type: Number,
      default: 110,
    },
    // 内圆背景色
    interBackground: {
      type: [Array, String],
      default: 'rgba(0,0,0,0.7)',
    },
    // 外圆半径
    outerRadius: {
      type: Number,
      default: 130,
    },
    // 外圆环直径
    outerWidth: {
      type: Number,
      default: 20,
    },
    // 外圆环背景色
    outerBackground: {
      type: String,
      default: 'rgba(75, 160, 174, 0.1)',
    },
    // 进度线宽度
    borderWidth: {
      type: Number,
      default: 5,
    },
    // 初始步长
    startStep: {
      type: Number,
      default: 1,
    },
    // 数值跳动步长
    step: {
      type: Number,
      default: 1,
    },
  });

  // 画圆:  Canvas对象：ctx  圆心坐标: (x, y)  半径: radius  起始角度: startAngle 结束角度: endAngle 是否逆时针旋转: false 代表顺时针旋转  画笔尺寸  是否闭合  画笔颜色  填充颜色
  const drawCircle = (
    ctx,
    x,
    y,
    radius,
    startAngle,
    endAngle,
    Boolean,
    size,
    closePath,
    strokeColor,
    fillColor
  ) => {
    ctx.beginPath(); //beginPath() 方法开始一条路径，或重置当前的路径。
    ctx.lineWidth = size; //画笔粗细
    ctx.strokeStyle = strokeColor; //画笔颜色
    ctx.arc(x, y, radius, startAngle, endAngle, Boolean); //路径
    closePath && ctx.closePath(); //结束路径
    if (fillColor) {
      if (isArray(fillColor)) {
        drawColor(
          ctx,
          props.width / 2 - props.innerRadius,
          props.height - props.innerRadius,
          props.width / 2 + props.innerRadius,
          props.height,
          fillColor[0],
          fillColor[1]
        );
      } else {
        ctx.fillStyle = fillColor;
      }
      ctx.fill();
    } else {
      ctx.stroke(); //开始画
    }
  };
  // 刻度值
  const drawText = (ctx, content, x, y) => {
    let splitStyle = {
      fontSize: 12,
      fontWeight: 400,
      fontFamily: 'DINPro',
      textBaseline: 'Alphabetic',
      color: 'rgba(255, 255, 255, 0.8)',
      textAlign: 'center',
      ...props.splitStyle,
    };
    ctx.font = `${splitStyle.fontWeight} ${splitStyle.fontSize}px ${splitStyle.fontFamily}`;
    ctx.fillStyle = splitStyle.color || props.color;
    ctx.textAlign = splitStyle.textAlign;
    ctx.textBaseline = splitStyle.textBaseline;
    ctx.fillText(`${content}`, x, y);
  };

  // 渐变色
  const drawColor = (ctx, x1, y1, x2, y2, color1, color2) => {
    let gradient = ctx.createLinearGradient(x1, y1, x2, y2);
    gradient.addColorStop(0, color1);
    gradient.addColorStop(1, color2);
    ctx.fillStyle = gradient;
  };
  // 清除圆环位置
  let stepClear = 1;
  const clearArc = (ctx, x, y, radius) => {
    //圆心(x,y)，半径radius
    let calcWidth = radius - stepClear;
    let calcHeight = Math.sqrt(radius * radius - calcWidth * calcWidth);

    let posX = x - calcWidth;
    let posY = y - calcHeight;

    let widthX = 2 * calcWidth;
    let heightY = 2 * calcHeight;

    if (stepClear <= radius) {
      ctx.clearRect(posX, posY, widthX, heightY);
      stepClear += 1;
      clearArc(x, y, radius);
    }
  };

  // 开始角度
  let startAngle = ((props.startAngle + 180) * Math.PI) / 180;
  // 结束角度
  let endAngle = ((props.endAngle + 180) * Math.PI) / 180;
  // 需要渲染角度
  let valueAngle = (((props.value / props.maxValue) * 180 + props.endAngle) * Math.PI) / 180;
  // 初始进度
  let steps = props.startStep;

  //动画函数
  const animate = (ctx) => {
    //执行平滑动画
    window.requestAnimationFrame(function () {
      //判断步子最终走多远的边界值，此值可以改
      if (stepAngle < valueAngle) {
        //该函数在边界内可以调用
        animate(ctx);
      }
    });
    //清空绘制内容
    ctx.clearRect(0, 0, props.width, props.height);
    //每次增加的步长，数值越大步子越大跑的越快，数值越小走的越慢
    if (steps + 1 > props.value && steps < props.value) {
      steps = props.value;
    } else {
      steps += props.step; //可改
    }
    let stepAngle = (((steps / props.maxValue) * 180 + props.endAngle) * Math.PI) / 180;
    //调用绘制形状函数，传入参数绘制对象，环形进度步长
    drawGauge(ctx, stepAngle, steps);
  };

  const drawGauge = (ctx, stepAngle, steps) => {
    //外圆背景
    drawCircle(
      ctx,
      props.width / 2,
      props.height,
      props.outerRadius,
      startAngle,
      endAngle,
      false,
      props.outerWidth,
      false,
      props.outerBackground
    );
    //内圆
    drawCircle(
      ctx,
      props.width / 2,
      props.height,
      props.innerRadius,
      startAngle,
      endAngle,
      false,
      0,
      true,
      'transparent',
      props.interBackground
    );
    // 刻度
    let total = props.maxValue;
    let r1 = props.outerRadius - props.outerWidth / 2 - 2; // 117
    let r2 = props.innerRadius + 2; //  112
    let r3 = props.innerRadius - props.outerWidth / 2 - (r1 - r2) - 2; // 95
    let angles = (props.endAngle - props.startAngle) / (props.splitNumber - 1);
    for (let i = 0; i < props.splitNumber; i++) {
      let angle = (angles * i * Math.PI) / 180;
      let startX = props.width / 2 - Math.cos(angle) * r1;
      let startY = props.width / 2 - Math.sin(angle) * r1;
      let endX = props.width / 2 - Math.cos(angle) * r2;
      let endY = props.width / 2 - Math.sin(angle) * r2;
      let textX = props.width / 2 - Math.cos(angle) * r3;
      let textY = props.width / 2 - Math.sin(angle) * r3;
      ctx.beginPath();
      ctx.strokeStyle = props.splitLineColor;
      ctx.lineWidth = props.splitWidth;
      ctx.moveTo(startX, startY);
      ctx.lineTo(endX, endY);
      ctx.stroke();
      drawText(ctx, (total / (props.splitNumber - 1)) * i, textX, textY);
    }
    if (isArray(props.backgroundColor)) {
      props.backgroundColor.forEach((v, i) => {
        let start =
          i === 0
            ? startAngle
            : ((props.backgroundColor[i - 1]?.offset * 180 + props.endAngle) * Math.PI) / 180;
        if (start < stepAngle) {
          // 进度条背景
          drawCircle(
            ctx,
            props.width / 2,
            props.height,
            props.outerRadius,
            start,
            stepAngle,
            false,
            props.outerWidth,
            false,
            v.color
          );
        }
      });
    } else {
      // 进度条背景
      drawCircle(
        ctx,
        props.width / 2,
        props.height,
        props.outerRadius,
        startAngle,
        stepAngle,
        false,
        props.outerWidth,
        false,
        props.backgroundColor
      );
    }
    if (isArray(props.color)) {
      props.color.forEach((v, i) => {
        v.startStep = i === 0 ? 0 : props.color[i - 1].endStep;
        v.endStep = i === props.color.length - 1 ? props.maxValue : v.offset * props.maxValue;
        let start =
          i === 0
            ? startAngle
            : ((props.color[i - 1].offset * 180 + props.endAngle) * Math.PI) / 180;
        if (start < stepAngle) {
          // 进度条线
          drawCircle(
            ctx,
            props.width / 2,
            props.height,
            props.outerRadius + props.outerWidth / 2,
            start,
            stepAngle,
            false,
            props.borderWidth,
            false,
            v.color
          );
        }
      });
    } else {
      // 进度条线
      drawCircle(
        ctx,
        props.width / 2,
        props.height,
        props.outerRadius + props.outerWidth / 2,
        startAngle,
        stepAngle,
        false,
        props.borderWidth,
        false,
        props.color
      );
    }
    // 绘制标题文字
    let fontStyle = {
      fontSize: 32,
      fontWeight: 500,
      fontFamily: 'DINPro-Medium',
      textBaseline: 'Alphabetic',
      color: '',
      textAlign: 'center',
      top: 0,
      bottom: 25,
      left: 0,
      right: 0,
      unit: '',
      ...props.titleStyle,
    };
    ctx.save();
    ctx.font = `${fontStyle.fontWeight} ${fontStyle.fontSize}px ${fontStyle.fontFamily}`;
    if (isArray(props.color)) {
      props.color.forEach((v) => {
        if (v.startStep < props.value && props.value <= v.endStep) {
          ctx.fillStyle = v.color;
        }
      });
    } else {
      ctx.fillStyle = fontStyle.color || props.color;
    }
    ctx.textAlign = fontStyle.textAlign;
    ctx.textBaseline = fontStyle.textBaseline;
    if (props.title) {
      ctx.fillText(
        props.title,
        fontStyle.left || props.width / 2 - fontStyle.right,
        fontStyle.top || props.height - fontStyle.bottom
      );
    } else {
      if (steps <= props.value) {
        ctx.fillText(
          steps + fontStyle.unit,
          fontStyle.left || props.width / 2 - fontStyle.right,
          fontStyle.top || props.height - fontStyle.bottom
        );
      }
    }
    ctx.restore();
    if (props.subTitle) {
      // 绘制副标题文字
      let subFontStyle = {
        fontSize: 12,
        fontWeight: 'normal',
        fontFamily: 'PingFang',
        textBaseline: 'Alphabetic',
        color: 'rgba(255, 255, 255, 0.8)',
        textAlign: 'center',
        top: 0,
        bottom: 5,
        left: 0,
        right: 0,
        ...props.subTitleStyle,
      };
      ctx.font = `${subFontStyle.fontWeight} ${subFontStyle.fontSize}px ${subFontStyle.fontFamily}`;
      ctx.fillStyle = subFontStyle.color || props.color;
      ctx.textAlign = subFontStyle.textAlign;
      ctx.textBaseline = subFontStyle.textBaseline;
      ctx.fillText(
        props.subTitle,
        subFontStyle.left || props.width / 2 - subFontStyle.right,
        subFontStyle.top || props.height - subFontStyle.bottom
      );
    }
  };

  setIndex();
  const canvasId = ref();
  canvasId.value = props.id || 'myCanvas' + getIndex();
  onMounted(() => {
    let myCanvas = document.getElementById(canvasId.value); //获取画布节点
    let ctx = myCanvas.getContext('2d'); //采用2d格式，并获取对象
    animate(ctx);
  });
</script>
